package com.itheima.stock.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.security.user.LoginUserDetail;
import com.itheima.stock.security.utils.JwtTokenUtil;
import com.itheima.stock.vo.req.LoginReqVo;
import com.itheima.stock.vo.resp.LoginRespExpandVo;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 认真过滤器，核心作用：认证用户信息，并颁发jwt的票据
 */
public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String USERNAME_KEY = "username";
    public static final String PASSWORD_KEY = "password";

    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 自定义构造器，传入登录认证的url地址
     * @param defaultFilterProcessesUrl
     */
    public JwtLoginAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    public void setRedisTemplate(RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }

    /**
     * 尝试去认证的方法
     * @param httpServletRequest
     * @param httpServletResponse
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        //必须是Post
        if(!httpServletRequest.getMethod().equalsIgnoreCase("POST")||!(MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(httpServletRequest.getContentType())||MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(httpServletRequest.getContentType()))){
            throw new AuthenticationServiceException("Authentication method not supported: " + httpServletRequest.getMethod());
        }
        //获取post请求ajax传入的数据流
        LoginReqVo reqVo = new ObjectMapper().readValue(httpServletRequest.getInputStream(),LoginReqVo.class);
        //校验输出的验证码是否正确
        //补充：根据传入的rkye从redis钟获取校验码
        String rChechCode = (String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + reqVo.getSessionId());
        //设置响应格式和编码
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpServletResponse.setCharacterEncoding("utf-8");
        if(rChechCode==null||!rChechCode.equalsIgnoreCase(reqVo.getCode())){
            //响应验证码输入错误
            R<Object> error = R.error(ResponseCode.CHECK_CODE_ERROR.getMessage());
            String jsonData = new ObjectMapper().writeValueAsString(error);
            //给前端相应错误的提示
            httpServletResponse.getWriter().write(jsonData);
            return null;
        }
        String username = reqVo.getUsername();
        username = (username!=null) ? username : "";
        username = username.trim();
        String password = reqVo.getPassword();
        password = (password != null) ? password : "";
        //组装认证的票据对象
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);
        //交给认证管理器认证票据对象
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        LoginUserDetail principal = ((LoginUserDetail) authResult.getPrincipal());
        String username = principal.getUsername();
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        //生成票据["P5","ROLE_ADMIN"]
        String tokenStr = JwtTokenUtil.createToken(username,authorities.toString());
        //响应数据格式json
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        //构建响应实体对象
        LoginRespExpandVo respExpandVo = new LoginRespExpandVo();
        BeanUtils.copyProperties(principal,respExpandVo);
        respExpandVo.setAccessToken(tokenStr);
        R<LoginRespExpandVo> ok = R.ok(respExpandVo);
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(ok));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        R<Object> error = R.error(ResponseCode.ERROR);
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(error));
    }
}
